package mh.demo.jdk.util.concurrent.locks;

import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.List;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.locks.ReentrantLock;

/**
 * 《Java并发编程艺术》书中的公平锁和非公平锁的示例
 */
public class FairAndUnFairDemo {

    private static final ReentrantLock2 fairLock = new ReentrantLock2(true);
    private static final ReentrantLock2 unFairLock = new ReentrantLock2(false);

    public static void main(String[] args) {
        FairAndUnFairDemo demo = new FairAndUnFairDemo();
        demo.runDemo(fairLock);
        System.out.println("-----------------------------------------------------------------------");
        demo.runDemo(unFairLock);
    }

    private void runDemo(ReentrantLock2 lock) {
        int count = 5;
        CountDownLatch latch = new CountDownLatch(count);
        for (int i = 0; i < count; i++) {
            new Job(lock, latch, i).start();
        }
        try {
            latch.await();
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }

    private static class ReentrantLock2 extends ReentrantLock {
        public ReentrantLock2(boolean fair) {
            super(fair);
        }

        public Collection<Thread> getQueueThreads() {
            List<Thread> arrayList = new ArrayList<>(super.getQueuedThreads());
            Collections.reverse(arrayList);
            return arrayList;
        }
    }

    private static class Job extends Thread {
        private final ReentrantLock2 lock;
        private final CountDownLatch latch;

        public Job(ReentrantLock2 lock, CountDownLatch latch, Integer i) {
            this.lock = lock;
            this.latch = latch;
            if (i != null) {
                super.setName(String.valueOf(i));
            }
        }

        @Override
        public void run() {
            for (int i = 0; i < 2; i++) {
                lock.lock();
                try {
                    System.out.printf("%s Lock by [%s], Waiting by %s\n",
                            lock.isFair() ? "Fair" : "Unfair", Thread.currentThread().getName(), lock.getQueueThreads());
                } finally {
                    lock.unlock();
                }
            }
            latch.countDown();
        }

        @Override
        public String toString() {
            return super.getName();
        }
    }
}
